package cn.you.GenghisKhan.common.parser;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import cn.you.GenghisKhan.common.utils.StringUtils;

public class Converters {
	public static boolean isCheckJsonType = false;
	private static final Set<String> baseFields = new HashSet();
	private static final Map<String, Field> fieldCache = new ConcurrentHashMap();
	static {
		baseFields.add("errorCode");
		baseFields.add("msg");
		baseFields.add("subCode");
		baseFields.add("subMsg");
		baseFields.add("body");
		baseFields.add("params");
		baseFields.add("success");
		baseFields.add("topForbiddenFields");
	}
	/**
	 * json转换对象
	 * @param clazz
	 * @param reader
	 * @param objectList
	 * @return
	 * @throws Exception
	 */
	public static <T> T convert(Class<T> clazz, Reader reader,
			List<ObjectConfig> objectList) throws Exception {
		T rsp = null;
		try {   
			rsp = clazz.newInstance();
			BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
			PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
			for (PropertyDescriptor pd : pds) {
				Method method = pd.getWriteMethod();
				if (method != null) {  
					String itemName = pd.getName();
					String listName = null;
					Field field;
					field = getField(clazz, pd);
					if (field != null) {
						ObjectConfig objconfig = ObjectConfig.GetObjectConfig(
								objectList, itemName);
						if (objconfig != null) {
							String[] nodes = objconfig.getNode().split("\\.");
							if (nodes.length == 2) {
								listName = nodes[0];
								itemName = nodes[1];
							} else {
								itemName = nodes[0];
							}
						}
						else
						{
							itemName="";
							
						}
						if ((reader.hasReturnField(itemName))
								|| ((listName != null) && (reader
										.hasReturnField(listName)))) {
							Class<?> typeClass = field.getType();
							if (String.class.isAssignableFrom(typeClass)) {
								Object value =null;
								Map<?, ?> jsonMap=null;
								//当listName不为空，获取listName对应的value
								if(((listName != null) && (reader.hasReturnField(listName)))){
									Object valueMap = reader.getPrimitiveObject(listName);
									//valueMap是否为map，获取itemName对应的value
									if(valueMap instanceof Map ){
										jsonMap= (Map<?, ?>) valueMap;
										value = jsonMap.get(itemName);
									}
								}else{
									value = reader.getPrimitiveObject(itemName);
								}
								if ((value instanceof String)) {
									method.invoke(rsp,
											new Object[] { value.toString() });
								} else {
									if ((isCheckJsonType) && (value != null)) {
										throw new Exception(itemName
												+ " is not a String");
									}
									if (value != null) {
										method.invoke(
												rsp,
												new Object[] { value.toString() });
									} else {
										method.invoke(rsp, new Object[] { "" });
									}
								}
							} else if (Long.class.isAssignableFrom(typeClass)
									|| long.class.isAssignableFrom(typeClass)) {
								Object value = reader
										.getPrimitiveObject(itemName);
								if ((value instanceof Long)) {
									method.invoke(rsp,
											new Object[] { (Long) value });
								} else {
									if ((isCheckJsonType) && (value != null)) {
										throw new Exception(itemName
												+ " is not a Number(Long)");
									}
									if (StringUtils.isNumeric(value)) {
										method.invoke(rsp, new Object[] { Long
												.valueOf(value.toString()) });
									}
								}
							} else if (Integer.class
									.isAssignableFrom(typeClass)
									|| int.class.isAssignableFrom(typeClass)) {
								Object value = reader
										.getPrimitiveObject(itemName);
								if ((value instanceof Integer)) {
									method.invoke(rsp,
											new Object[] { (Integer) value });
								} else {
									if ((isCheckJsonType) && (value != null)) {
										throw new Exception(itemName
												+ " is not a Number(Integer)");
									}
									if (StringUtils.isNumeric(value)) {
										method.invoke(rsp,
												new Object[] { Integer
														.valueOf(value
																.toString()) });
									}
								}
							} else if (Boolean.class
									.isAssignableFrom(typeClass)
									|| boolean.class
											.isAssignableFrom(typeClass)) {
								Object value = reader
										.getPrimitiveObject(itemName);
								if ((value instanceof Boolean)) {
									method.invoke(rsp,
											new Object[] { (Boolean) value });
								} else {
									if ((isCheckJsonType) && (value != null)) {
										throw new Exception(itemName
												+ " is not a Boolean");
									}
									if (value != null) {
										method.invoke(rsp,
												new Object[] { Boolean
														.valueOf(value
																.toString()) });
									}
								}
							} else if (Double.class.isAssignableFrom(typeClass)
									|| double.class.isAssignableFrom(typeClass)) {
								Object value = reader
										.getPrimitiveObject(itemName);
								
								if ((value instanceof Double)) {
									method.invoke(rsp,
											new Object[] { (Double) value });
								}
								else
								{
									try{
									Double d = Double.valueOf(value.toString());
									method.invoke(rsp,
											new Object[] { d });
									}
									catch(Exception x)
									{
										throw new Exception(itemName
												+ " is not a Double");
									}
								}
							 
							} else if (Number.class.isAssignableFrom(typeClass)) {
								Object value = reader
										.getPrimitiveObject(itemName);
								if ((value instanceof Number)) {
									method.invoke(rsp,
											new Object[] { (Number) value });
								} else if ((isCheckJsonType) && (value != null)) {
									throw new Exception(itemName
											+ " is not a Number");
								}
							} else if (Date.class.isAssignableFrom(typeClass)) {
								DateFormat format = new SimpleDateFormat(
										"yyyy-MM-dd HH:mm:ss");
								format.setTimeZone(TimeZone
										.getTimeZone("GMT+8"));
								Object value = reader
										.getPrimitiveObject(itemName);
								if ((value instanceof String)) {
									method.invoke(rsp, new Object[] { format
											.parse(value.toString()) });
								}
							} else if (List.class.isAssignableFrom(typeClass)) {
								Type fieldType = field.getGenericType();
								if ((fieldType instanceof ParameterizedType)) {
									ParameterizedType paramType = (ParameterizedType) fieldType;
									Type[] genericTypes = paramType
											.getActualTypeArguments();
									if ((genericTypes != null)
											&& (genericTypes.length > 0)
											&& ((genericTypes[0] instanceof Class))) {
										Class<?> subType = (Class) genericTypes[0];
										List<?> listObjs = reader
												.getListObjects(
														listName,
														itemName,
														subType,
														objconfig
																.getChildnodes());
										if (listObjs != null) {
											method.invoke(rsp,
													new Object[] { listObjs });
										}
									}
								}
							} else {
								Object obj = reader.getObject(itemName,
										typeClass);
								if (obj != null) {
									method.invoke(rsp, new Object[] { obj });
								}
							}
						}
					}
				}
			}
		} catch (Exception e) {
			throw e;
		}
		return rsp;
	}

	public static boolean isCheckJsonType() {
		return isCheckJsonType;
	}

	public static void setCheckJsonType(boolean isCheckJsonType) {
		Converters.isCheckJsonType = isCheckJsonType;
	}

	public static Set<String> getBasefields() {
		return baseFields;
	}

	public static Map<String, Field> getFieldcache() {
		return fieldCache;
	}

	public static <T> T convert(Class<T> clazz, Reader reader) throws Exception {
		T rsp = null;
		try {
			rsp = clazz.newInstance();
			BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
			PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
			for (PropertyDescriptor pd : pds) {
				Method method = pd.getWriteMethod();
				if (method != null) {
					String itemName = pd.getName();
					String listName = null;
					Field field;
					field = getField(clazz, pd);
					if (field != null) {
						ApiField jsonField = (ApiField) field
								.getAnnotation(ApiField.class);
						if (jsonField != null) {
							itemName = jsonField.value();
						}
						ApiListField jsonListField = (ApiListField) field
								.getAnnotation(ApiListField.class);
						if (jsonListField != null) {
							listName = jsonListField.value();
						}
						if ((reader.hasReturnField(itemName))
								|| ((listName != null) && (reader
										.hasReturnField(listName)))) {
							Class<?> typeClass = field.getType();
							if (String.class.isAssignableFrom(typeClass)) {
								
								Object value = reader
										.getPrimitiveObject(itemName);
								if ((value instanceof String)) {
									method.invoke(rsp,
											new Object[] { value.toString() });
								} else {
									if ((isCheckJsonType) && (value != null)) {
										throw new Exception(itemName
												+ " is not a String");
									}
									if (value != null) {
										method.invoke(
												rsp,
												new Object[] { value.toString() });
									} else {
										method.invoke(rsp, new Object[] { "" });
									}
								}
							} else if (Long.class.isAssignableFrom(typeClass)
									|| long.class.isAssignableFrom(typeClass)) {
								Object value = reader
										.getPrimitiveObject(itemName);
								if ((value instanceof Long)) {
									method.invoke(rsp,
											new Object[] { (Long) value });
								} else {
									if ((isCheckJsonType) && (value != null)) {
										throw new Exception(itemName
												+ " is not a Number(Long)");
									}
									if (StringUtils.isNumeric(value)) {
										method.invoke(rsp, new Object[] { Long
												.valueOf(value.toString()) });
									}
								}
							} else if (Integer.class
									.isAssignableFrom(typeClass)
									|| int.class.isAssignableFrom(typeClass)) {
								Object value = reader
										.getPrimitiveObject(itemName);
								if ((value instanceof Integer)) {
									method.invoke(rsp,
											new Object[] { (Integer) value });
								} else {
									if ((isCheckJsonType) && (value != null)) {
										throw new Exception(itemName
												+ " is not a Number(Integer)");
									}
									if (StringUtils.isNumeric(value)) {
										method.invoke(rsp,
												new Object[] { Integer
														.valueOf(value
																.toString()) });
									}
								}
							} else if (Boolean.class
									.isAssignableFrom(typeClass)
									|| boolean.class
											.isAssignableFrom(typeClass)) {
								Object value = reader
										.getPrimitiveObject(itemName);
								if ((value instanceof Boolean)) {
									method.invoke(rsp,
											new Object[] { (Boolean) value });
								} else {
									if ((isCheckJsonType) && (value != null)) {
										throw new Exception(itemName
												+ " is not a Boolean");
									}
									if (value != null) {
										method.invoke(rsp,
												new Object[] { Boolean
														.valueOf(value
																.toString()) });
									}
								}
							} else if (Double.class.isAssignableFrom(typeClass)
									|| double.class.isAssignableFrom(typeClass)) {
								Object value = reader
										.getPrimitiveObject(itemName);
								if ((value instanceof Double)) {
									method.invoke(rsp,
											new Object[] { (Double) value });
								} else if ((isCheckJsonType) && (value != null)) {
									throw new Exception(itemName
											+ " is not a Double");
								}
							} else if (Number.class.isAssignableFrom(typeClass)) {
								Object value = reader
										.getPrimitiveObject(itemName);
								if ((value instanceof Number)) {
									method.invoke(rsp,
											new Object[] { (Number) value });
								} else if ((isCheckJsonType) && (value != null)) {
									throw new Exception(itemName
											+ " is not a Number");
								}
							} else if (Date.class.isAssignableFrom(typeClass)) {
								DateFormat format = new SimpleDateFormat(
										"yyyy-MM-dd HH:mm:ss");
								format.setTimeZone(TimeZone
										.getTimeZone("GMT+8"));
								Object value = reader
										.getPrimitiveObject(itemName);
								if ((value instanceof String)) {
									method.invoke(rsp, new Object[] { format
											.parse(value.toString()) });
								}
							} else if (List.class.isAssignableFrom(typeClass)) {
								Type fieldType = field.getGenericType();
								if ((fieldType instanceof ParameterizedType)) {
									ParameterizedType paramType = (ParameterizedType) fieldType;
									Type[] genericTypes = paramType
											.getActualTypeArguments();
									if ((genericTypes != null)
											&& (genericTypes.length > 0)
											&& ((genericTypes[0] instanceof Class))) {
										Class<?> subType = (Class) genericTypes[0];
										List<?> listObjs = reader
												.getListObjects(listName,
														itemName, subType, null);
										if (listObjs != null) {
											method.invoke(rsp,
													new Object[] { listObjs });
										}
									}
								}
							} else {
								Object obj = reader.getObject(itemName,
										typeClass);
								if (obj != null) {
									method.invoke(rsp, new Object[] { obj });
								}
							}
						}
					}
				}
			}
		} catch (Exception e) {
			throw e;
		}
		return rsp;
	}

	private static Field getField(Class<?> clazz, PropertyDescriptor pd)
			throws Exception {
		String key = clazz.getName() + "_" + pd.getName();
		Field field = (Field) fieldCache.get(key);
		if (field == null) {
			try {
				field = clazz.getDeclaredField(pd.getName());
				fieldCache.put(key, field);
			} catch (NoSuchFieldException e) {
				return null;
			}
		}
		return field;
	}
}
